﻿using System;
using System.IO.Pipes;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;

namespace Yt.Core.IPC.NamedPipe
{
    /// <summary>
    /// 管道连接事件
    /// </summary>
    public delegate void ConnectionHandler();
    /// <summary>
    /// 管道异常事件
    /// </summary>
    public delegate void ErrorHandler(Exception exception);
    /// <summary>
    /// 命名管道基类
    /// </summary>
    internal abstract class PipeBuilderBase : IDisposable
    {
        protected static readonly object locker = new object();
        protected PipeStream pipeStream { get; set; }
        protected StreamWriter streamWriter { get; set; }
        protected StreamReader streamReader { get; set; }
        internal Action<string> CallBack { get; set; }
        public string Name { get; set; }
        public Dictionary<string, object> Services = new Dictionary<string, object>();
        public bool IsDisposing = false;
        public ConnectionHandler OnConnection;
        public ConnectionHandler OnDisconnect;
        public ErrorHandler OnError;
        protected PipeBuilderBase(string pipeName)
        {
            Name = pipeName;
        }
        ~PipeBuilderBase()
        {
            Dispose();
        }
        /// <summary>
        /// 检索type类型的关联实例并调用实例对应的方法
        /// </summary>
        /// <param name="type">要调用的接口或类类型</param>
        /// <param name="methodName">方法名</param>
        /// <param name="pArgs">方法参数</param>
        /// <returns></returns>
        public virtual object Invoker(Type type, string methodName, List<object> pArgs)
        {
            if (!Services.ContainsKey(type.FullName)) throw new NotImplementedException("代理映射中不存在接口" + type.FullName + ";MethodName:" + methodName);
            object service = Services[type.FullName];
            MethodInfo info = service.GetType().GetMethod(methodName);
            if (info == null || !info.IsPublic) throw new NotImplementedException(methodName + "方法不存在");
            var pinfos = info.GetParameters();
            if (pinfos.Length != pArgs.Count) throw new ArgumentOutOfRangeException("参数错误");
            object[] realArgs = pArgs.ToArray();

            //处理枚举类型
            if (pinfos.Length > 0)
            {
                for (int i = 0; i < pinfos.Length; i++)
                {
                    var pinfo = pinfos[i];
                    if (pinfo.ParameterType.BaseType == typeof(Enum))
                    {
                        realArgs[i] = Enum.Parse(pinfo.ParameterType, realArgs[i].ToString());
                    }
                }
            }

            object obj = null;
            try
            {
                obj = info.Invoke(service, realArgs);
            }
            catch (Exception ex)
            {
                if (ex.InnerException != null)
                {
                    OnError?.Invoke(ex.InnerException);
                    throw ex.InnerException;
                }
                else
                {
                    OnError?.Invoke(ex);
                    throw ex;
                }
            }
            return obj;
        }
        /// <summary>
        /// 发送字符串给管道另一端
        /// </summary>
        /// <param name="msg">待发送字符串</param>
        public virtual void Send(string msg, Action callBack = null)
        {
            try
            {
                if (!pipeStream.IsConnected)
                {
                    OnError?.BeginInvoke(new Exception("连接已关闭"), null, null);
                    return;
                }
                lock(locker)
                {
                    string base64Str = JsonSerializeHelper.ToBase64String(msg);
                    streamWriter.WriteLine(base64Str);
                    streamWriter.Flush();
                    pipeStream.WaitForPipeDrain();
                    callBack?.BeginInvoke(null, null);
                }
               
            }
            catch (Exception ex)
            {
                OnError?.BeginInvoke(ex, null, null);
            }
        }
        internal virtual void DoReadLine()
        {
            while (pipeStream.IsConnected || !IsDisposing)
            {
                try
                {
                    Task<string> task = streamReader.ReadLineAsync();
                    if (task.Result == null) continue;
                    string json = JsonSerializeHelper.Base64StringToJson(task.Result);
                    Message message = JsonSerializeHelper.ToObject<Message>(json);
                    if (message == null) continue;
                    switch (message.MessageType)
                    {
                        case MessageType.Request:
                            var messageData = JsonSerializeHelper.ToObject<MessageData>(message.Data);
                            object obj = Invoker(Type.GetType(messageData.TypeName), messageData.MethodName, messageData.Args);
                            Message returmmessage = new Message();
                            returmmessage.MessageType = MessageType.Response;
                            returmmessage.Data = JsonSerializeHelper.ToJson(obj);
                            Send(JsonSerializeHelper.ToJson(returmmessage));
                            break;
                        case MessageType.NoReplyRequest:
                            var messageDataNoReply = JsonSerializeHelper.ToObject<MessageData>(message.Data);
                            Invoker(Type.GetType(messageDataNoReply.TypeName), messageDataNoReply.MethodName, messageDataNoReply.Args);
                            break;
                        case MessageType.Response:
                            CallBack?.Invoke(message.Data);
                            break;
                        case MessageType.CancellationRequest:
                            break;
                        default:
                            break;
                    }
                }
                catch (Exception ex)
                {
                    OnError?.BeginInvoke(ex, null, null);
                }
            }
        }

        /// <summary>
        /// 连接管道，开始工作,用于服务端
        /// </summary>
        public virtual void Connect()
        {
        }
        /// <summary>
        /// 关联接口与对象
        /// </summary>
        /// <typeparam name="Interface">接口类型</typeparam>
        /// <param name="service">实现接口的对象</param>
        public void AddService<Interface>(object service)
        {
            if (service == null) return;

            var serviceKey = typeof(Interface).FullName;
            if (!Services.ContainsKey(serviceKey))
            {
                Services.Add(serviceKey, service);
            }
        }
      
        /// <summary>
        /// 移除关联对象
        /// </summary>
        /// <typeparam name="Interface">接口类型</typeparam>
        public void RemoveService<Interface>()
        {
            if (Services.ContainsKey(typeof(Interface).FullName))
            { Services.Remove(typeof(Interface).FullName); }
        }
        /// <summary>
        /// 移除关联对象
        /// </summary>
        /// <param name="service">待移除的对象</param>
        public void RemoveService(object service)
        {
            if (service == null) return;
            var keys = Services.Keys.ToList();
            foreach (string key in keys)
            {
                if (Services[key] == service)
                { Services.Remove(key); }
            }
        }
        public virtual void Dispose()
        {
            IsDisposing = true;
            try
            {
                Services.Clear();
                pipeStream?.Dispose();
                streamWriter?.Close();
                streamReader?.Close();
            }
            catch (Exception)
            {
            }
        }
    }
}
